﻿using Microsoft.VisualStudio.TestTools.UnitTesting;
using Sustainalytics.Entities;
using Sustainalytics.Utils;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;

namespace Sustainalytics.DataLayer.Tests
{
    [TestClass]
    public class AccountsRepositoryTests
    {
        private const string _connectionString = "mongodb://localhost:27017/ClientPlatform_Tests";
        private IAccountsRepository _repository;

        [TestInitialize]
        public void Initialize()
        {
            _repository = AccountsRepository.GetAccountsRepository(_connectionString);
        }

        [TestCleanup]
        public void Cleanup()
        {
            _repository.Cleanup();
        }

        [TestMethod]
        public void GetAccounts()
        {
            var accounts = new List<Account>()
            {
                new Account()
                {
                    Id = Guid.NewGuid(),
                    AccountManager =
                        new AccountManager()
                        {
                            Email = "testemail@sustainalytics.com",
                            FirstName = "ManagerFirstName",
                            LastName = "ManagerLastName",
                            Title = "ManagerTitle"
                        },
                    ModifiedOn = DateTime.UtcNow,
                    Name = "TestAccountName",
                    Office = "Bucharest",
                    RelationshipType = RelationshipType.Client,
                    Status = Status.Active
                },
                new Account()
                {
                    Id = Guid.NewGuid(),
                    AccountManager =
                        new AccountManager()
                        {
                            Email = "testemail@sustainalytics.com",
                            FirstName = "ManagerFirstName",
                            LastName = "ManagerLastName",
                            Title = "ManagerTitle"
                        },
                    ModifiedOn = DateTime.UtcNow,
                    Name = "AccountName",
                    Office = "Bucharest",
                    RelationshipType = RelationshipType.Client,
                    Status = Status.Active
                }
            };
            _repository.SaveAccounts(accounts);
            ListOptions listOptions = new ListOptions();

            var resultedAccounts = _repository.GetAccounts(null, "test", listOptions);

            Assert.IsTrue(resultedAccounts.Count() == 1);
            Assert.IsTrue(resultedAccounts.First().Status == Status.Active);
        }

        [TestMethod]
        public void GetAccountsTotalCount()
        {
            var accounts = new List<Account>()
            {
                new Account()
                {
                    Id = Guid.NewGuid(),
                    AccountManager =
                        new AccountManager()
                        {
                            Email = "testemail@sustainalytics.com",
                            FirstName = "ManagerFirstName",
                            LastName = "ManagerLastName",
                            Title = "ManagerTitle"
                        },
                    ModifiedOn = DateTime.UtcNow,
                    Name = "TestAccountName 2",
                    Office = "Bucharest",
                    RelationshipType = RelationshipType.Client,
                    Status = Status.Active
                },
                new Account()
                {
                    Id = Guid.NewGuid(),
                    AccountManager =
                        new AccountManager()
                        {
                            Email = "testemail@sustainalytics.com",
                            FirstName = "ManagerFirstName",
                            LastName = "ManagerLastName",
                            Title = "ManagerTitle"
                        },
                    ModifiedOn = DateTime.UtcNow,
                    Name = "AccountName",
                    Office = "Bucharest",
                    RelationshipType = RelationshipType.Client,
                    Status = Status.Inactive
                }
            };
            _repository.SaveAccounts(accounts);

            var resultedAccounts = _repository.GetAccountsTotalCount(Status.Inactive, "");
            Assert.IsTrue(resultedAccounts == 1);
        }

        [TestMethod]
        public void SavePassword()
        {
            var newPassword = "Password99#";
            var userName = "mihai.stanescu@sustainalytics.com";
            var userId = Guid.NewGuid();
            var expirationDate = DateTime.Now;
            var connectionString = "mongodb://localhost/ClientPlatform_Tests";
            var accountsRepository = AccountsRepository.GetAccountsRepository(connectionString);

            List<User> users = new List<User>();
            users.Add(new User()
            {
                Id = userId,
                Email = userName,
                Account = "Timisoara",
                LastName = "Stanescu",
                FirstName = "Mihai",
                AccountId = Guid.NewGuid(),
                Status = Status.Active,
                FullName = "Mihai Stanescu",
                ModifiedOn = DateTime.Now
            });
            List<ClientUser> clientUsers = new List<ClientUser>();
            clientUsers.Add(new ClientUser()
            {
                Id = userId,
                Password = newPassword,
                PasswordExpirationDate = expirationDate
            });
            accountsRepository.SaveUsers(users);
            accountsRepository.SaveClientUsers(clientUsers);

            accountsRepository.SavePasswordByUsername(userName, newPassword, expirationDate.AddDays(1));
            var user = accountsRepository.GetClientUserByUsername(userName);

            Assert.AreEqual(user.Password, newPassword, string.Format("The saved password: {0} does not match with the input one: {1}.", user.Password, newPassword));
            Assert.AreEqual(user.PasswordExpirationDate.Date, expirationDate.AddDays(1).Date, string.Format("The expiration date: {0} is different than the expected one: {1}.", user.PasswordExpirationDate, expirationDate));
        }

        [TestMethod]
        public void ListAccountsIds()
        {
            var accounts = new List<Account>
            {
                new Account
                {
                    Id = Guid.NewGuid(),
                    Name = "Account1",
                    AccountManager = new AccountManager
                    {
                        Email = "Email1",
                        Title = "Dr.1",
                        FirstName = "FirstName1",
                        LastName = "LastName1",
                        FullName = "FullNamn1"
                    },
                    Office = "Office1",
                    RelationshipType = RelationshipType.Client,
                    Status = Status.Active,
                    ModifiedOn = DateTime.Now
                },
                new Account
                {
                    Id = Guid.NewGuid(),
                    Name = "Account2",
                    AccountManager = new AccountManager
                    {
                        Email = "Email2",
                        Title = "Dr.2",
                        FirstName = "FirstName2",
                        LastName = "LastName2",
                        FullName = "FullNamn2"
                    },
                    Office = "Office2",
                    RelationshipType = RelationshipType.Prospect,
                    Status = Status.Active,
                    ModifiedOn = DateTime.Now
                },
                new Account
                {
                    Id = Guid.NewGuid(),
                    Name = "Account3",
                    AccountManager = new AccountManager
                    {
                        Email = "Email3",
                        Title = "Dr.3",
                        FirstName = "FirstName3",
                        LastName = "LastName3",
                        FullName = "FullNamn3"
                    },
                    Office = "Office3",
                    RelationshipType = RelationshipType.Client,
                    Status = Status.Inactive,
                    ModifiedOn = DateTime.Now
                },
                new Account
                {
                    Id = Guid.NewGuid(),
                    Name = "Account4",
                    AccountManager = new AccountManager
                    {
                        Email = "Email4",
                        Title = "Dr.4",
                        FirstName = "FirstName4",
                        LastName = "LastName4",
                        FullName = "FullNamn4"
                    },
                    Office = "Office4",
                    RelationshipType = RelationshipType.Client,
                    Status = Status.Active,
                    ModifiedOn = DateTime.Now
                }
            };

            _repository.SaveAccounts(accounts);
            var businessAccountsIds = _repository.ListAccountsIds();

            Assert.IsNotNull(businessAccountsIds, "The method returned NULL!");
            Assert.IsTrue(businessAccountsIds.Count == accounts.Count, "The collection of statuses has a different number of elements than the source collection!");
            Assert.IsTrue(accounts.TrueForAll(a => businessAccountsIds.Contains(a.Id)), "There are accounts in the original collection which don't have a corresponding status entry!");
        }

        [TestMethod]
        public void DeactivateAccounts()
        {
            var accounts = new List<Account>
            {
                new Account
                {
                    Id = Guid.NewGuid(),
                    Name = "Account1",
                    AccountManager = new AccountManager
                    {
                        Email = "Email1",
                        Title = "Dr.1",
                        FirstName = "FirstName1",
                        LastName = "LastName1",
                        FullName = "FullNamn1"
                    },
                    Office = "Office1",
                    RelationshipType = RelationshipType.Client,
                    Status = Status.Active,
                    ModifiedOn = DateTime.Now
                },
                new Account
                {
                    Id = Guid.NewGuid(),
                    Name = "Account2",
                    AccountManager = new AccountManager
                    {
                        Email = "Email2",
                        Title = "Dr.2",
                        FirstName = "FirstName2",
                        LastName = "LastName2",
                        FullName = "FullNamn2"
                    },
                    Office = "Office2",
                    RelationshipType = RelationshipType.Prospect,
                    Status = Status.Active,
                    ModifiedOn = DateTime.Now
                },
                new Account
                {
                    Id = Guid.NewGuid(),
                    Name = "Account3",
                    AccountManager = new AccountManager
                    {
                        Email = "Email3",
                        Title = "Dr.3",
                        FirstName = "FirstName3",
                        LastName = "LastName3",
                        FullName = "FullNamn3"
                    },
                    Office = "Office3",
                    RelationshipType = RelationshipType.Client,
                    Status = Status.Inactive,
                    ModifiedOn = DateTime.Now
                },
                new Account
                {
                    Id = Guid.NewGuid(),
                    Name = "Account4",
                    AccountManager = new AccountManager
                    {
                        Email = "Email4",
                        Title = "Dr.4",
                        FirstName = "FirstName4",
                        LastName = "LastName4",
                        FullName = "FullNamn4"
                    },
                    Office = "Office4",
                    RelationshipType = RelationshipType.Client,
                    Status = Status.Active,
                    ModifiedOn = DateTime.Now
                }
            };

            _repository.SaveAccounts(accounts);
            _repository.DeactivateAccounts(accounts.Select(a => a.Id).ToList(), DateTime.UtcNow);

            var dbAccounts = new List<Account>();
            accounts.ForEach(a => dbAccounts.Add(_repository.GetAccountById(a.Id)));

            Assert.IsTrue(dbAccounts.All(a => a.Status == Status.Inactive), "Not all accounts were deactivated!");
        }

        [TestMethod]
        public void SaveAccounts()
        {
            var accounts = new List<Account>
            {
                new Account
                {
                    Id = Guid.NewGuid(),
                    Name = "Account1",
                    AccountManager = new AccountManager
                    {
                        Email = "Email1",
                        Title = "Dr.1",
                        FirstName = "FirstName1",
                        LastName = "LastName1",
                        FullName = "FullNamn1"
                    },
                    Office = "Office1",
                    RelationshipType = RelationshipType.Client,
                    Status = Status.Active,
                    ModifiedOn = DateTime.Now
                },
                new Account
                {
                    Id = Guid.NewGuid(),
                    Name = "Account2",
                    AccountManager = new AccountManager
                    {
                        Email = "Email2",
                        Title = "Dr.2",
                        FirstName = "FirstName2",
                        LastName = "LastName2",
                        FullName = "FullNamn2"
                    },
                    Office = "Office2",
                    RelationshipType = RelationshipType.Prospect,
                    Status = Status.Active,
                    ModifiedOn = DateTime.Now
                },
                new Account
                {
                    Id = Guid.NewGuid(),
                    Name = "Account3",
                    AccountManager = new AccountManager
                    {
                        Email = "Email3",
                        Title = "Dr.3",
                        FirstName = "FirstName3",
                        LastName = "LastName3",
                        FullName = "FullNamn3"
                    },
                    Office = "Office3",
                    RelationshipType = RelationshipType.Client,
                    Status = Status.Inactive,
                    ModifiedOn = DateTime.Now
                },
                new Account
                {
                    Id = Guid.NewGuid(),
                    Name = "Account4",
                    AccountManager = new AccountManager
                    {
                        Email = "Email4",
                        Title = "Dr.4",
                        FirstName = "FirstName4",
                        LastName = "LastName4",
                        FullName = "FullNamn4"
                    },
                    Office = "Office4",
                    RelationshipType = RelationshipType.Client,
                    Status = Status.Active,
                    ModifiedOn = DateTime.Now
                }
            };

            _repository.SaveAccounts(accounts);

            var dbAccounts = new List<Account>();
            accounts.ForEach(a => dbAccounts.Add(_repository.GetAccountById(a.Id)));

            Assert.IsTrue(dbAccounts.Count == accounts.Count, "The collection of statuses has a different number of elements than the source collection!");
            Assert.IsTrue(accounts.TrueForAll(a => dbAccounts.Any(sa => sa.Id == a.Id)), "Not all the accounts were saved successfully!");
        }

        [TestMethod]
        public void GetLastSyncDate()
        {
            Assert.AreEqual(_repository.GetLastSyncDate(SyncType.Account).ToString(CultureInfo.InvariantCulture), new DateTime(1900, 1, 1).ToString(CultureInfo.InvariantCulture), "The last sync date returned is invalid!");// in case no previous synchronization data exists in data store, the minimum date is assumed to be the last sync date
        }

        [TestMethod]
        public void SaveLastSync()
        {
            var accountsLastSync = new Synchronization { Id = SyncType.Account, LastSuccessfulSync = DateTime.UtcNow.AddDays(-3) };
            var usersLastSync = new Synchronization { Id = SyncType.User, LastSuccessfulSync = DateTime.UtcNow.AddDays(-7) };

            _repository.SaveLastSync(accountsLastSync);
            _repository.SaveLastSync(usersLastSync);

            var accountsLastSyncDate = _repository.GetLastSyncDate(SyncType.Account);
            var usersLastSyncDate = _repository.GetLastSyncDate(SyncType.User);

            Assert.AreEqual(accountsLastSyncDate.ToString(CultureInfo.InvariantCulture), accountsLastSync.LastSuccessfulSync.ToString(CultureInfo.InvariantCulture), "The last accounts sync date returned is invalid!");
            Assert.AreEqual(usersLastSyncDate.ToString(CultureInfo.InvariantCulture), usersLastSync.LastSuccessfulSync.ToString(CultureInfo.InvariantCulture), "The last users sync date returned is invalid!");
        }

        [TestMethod]
        public void GetUsers()
        {
            var users = new List<User>()
            {
                new User()
                {
                    Id = Guid.NewGuid(),
                    AccountId = new Guid("8d8b02ac-66b7-4ea6-947b-4acc3dc914e5"),
                    FullName = "Test User Name1",
                    Status = Status.Active,
                    Email = "luciana.raduti@sustainalytics.com"
                },
                new User()
                {
                    Id = Guid.NewGuid(),
                    AccountId = new Guid("8d8b02ac-66b7-4ea6-947b-4acc3dc914e5"),
                    FullName = "Test User Name2",
                    Status = Status.Active,
                    Email = "iosif.petre@sustainalytics.com"
                },
                new User()
                {
                    Id = Guid.NewGuid(),
                    AccountId = new Guid("8d8b02ac-66b7-4ea6-947b-4acc3dc914e5"),
                    FullName = "Test User Name3",
                    Status = Status.Active,
                    Email = "luciana.raduti@sustainalytics.com"
                },
                new User()
                {
                    Id = Guid.NewGuid(),
                    AccountId = new Guid("8d8b02ac-66b7-4ea6-947b-4acc3dc914e5"),
                    FullName = "Test User Name4",
                    Status = Status.Inactive,
                    Email = "luciana.raduti@sustainalytics.com"
                },
                new User()
                {
                    Id = Guid.NewGuid(),
                    AccountId = new Guid("8d8b02ac-66b7-4ea6-947b-4acc3dc914e5"),
                    FullName = "Test User Name5",
                    Status = Status.Active,
                    Email = "luciana.raduti@sustainalytics.com"
                }
            };
            _repository.SaveUsers(users);

            ListOptions listOptions = new ListOptions();
            var resultedUsers = _repository.GetUsers(new List<Guid>() { (Guid)users.First().AccountId }, Status.Inactive, "", listOptions);
            Assert.IsTrue(resultedUsers.Count() == 1);
            foreach (var resultedUser in resultedUsers)
            {
                Assert.IsTrue(resultedUser.Status == Status.Inactive);
                Assert.IsTrue(resultedUser.AccountId == users.First().AccountId);
            }
        }

        [TestMethod]
        public void GetUsersTotalCount()
        {
            var users = new List<User>()
            {
                new User()
                {
                    Id = Guid.NewGuid(),
                    AccountId = new Guid("8d8b02ac-66b7-4ea6-947b-4acc3dc914e5"),
                    FullName = "Test User Name3",
                    Status = Status.Inactive,
                    Email = "test@sustainalytics.com"
                }
            };
            _repository.SaveUsers(users);
            var resultedUsers = _repository.GetUsersTotalCount(new List<Guid>() { (Guid)users.First().AccountId }, Status.Inactive, "");
            Assert.IsTrue(resultedUsers == 1);
        }

        [TestMethod]
        public void GetAccountById()
        {
            var accounts = new List<Account>()
            {
                new Account()
                {
                    Id = Guid.NewGuid(),
                    AccountManager =
                        new AccountManager()
                        {
                            Email = "testemail@sustainalytics.com",
                            FirstName = "ManagerFirstName",
                            LastName = "ManagerLastName",
                            Title = "ManagerTitle"
                        },
                    ModifiedOn = DateTime.UtcNow,
                    Name = "TestAccountName",
                    Office = "Bucharest",
                    RelationshipType = RelationshipType.Client,
                    Status = Status.Active
                }
            };
            _repository.SaveAccounts(accounts);
            var resultedAccount = _repository.GetAccountById(accounts.First().Id);
            Assert.IsTrue(resultedAccount != null);
            Assert.IsTrue(resultedAccount.Id == accounts.First().Id);
        }

        [TestMethod]
        public void GetUserByUserId()
        {
            var connectionString = "mongodb://localhost/ClientPlatform_Tests";
            var accountsRepository = AccountsRepository.GetAccountsRepository(connectionString);

            List<User> users = new List<User>();
            User user1 = new User()
            {
                Id = Guid.NewGuid(),
                Email = "william.zombori@sustainalytics.com",
                Account = "Timisoara",
                LastName = "Zombori",
                FirstName = "William",
                AccountId = Guid.NewGuid(),
                Status = Status.Active,
                FullName = "William Zombori",
                ModifiedOn = DateTime.Now
            };
            users.Add(user1);

            User user2 = new User()
            {
                Id = Guid.NewGuid(),
                Email = "mihai.stanescu@sustainalytics.com",
                Account = "Arad",
                LastName = "Stanescu",
                FirstName = "Mihai",
                AccountId = Guid.NewGuid(),
                Status = Status.Active,
                FullName = "Mihai Stanescu",
                ModifiedOn = DateTime.Now
            };
            users.Add(user2);

            User user3 = new User()
            {
                Id = Guid.NewGuid(),
                Email = "iulian.negoita@sustainalytics.com",
                Account = "Lugoj",
                LastName = "Negoita",
                FirstName = "Iulian",
                AccountId = Guid.NewGuid(),
                Status = Status.Active,
                FullName = "Iulian Negoita",
                ModifiedOn = DateTime.Now
            };
            users.Add(user3);

            accountsRepository.SaveUsers(users);

            var user = accountsRepository.GetUserByUserId(user1.Id);

            Assert.IsNotNull(user);
            Assert.AreEqual(user.Id, user1.Id);
            Assert.AreEqual(user.FullName, user1.FullName);
            Assert.AreEqual(user.Email, user1.Email);
            Assert.AreEqual(user.Account, user1.Account);
            Assert.AreEqual(user.AccountId, user1.AccountId);
        }

        [TestMethod]
        public void GetClientUserByUserId()
        {
            var connectionString = "mongodb://localhost/ClientPlatform_Tests";
            var accountsRepository = AccountsRepository.GetAccountsRepository(connectionString);

            List<ClientUser> clientUsers = new List<ClientUser>();
            ClientUser clientUser1 = new ClientUser()
            {
                Id = Guid.NewGuid(),
                Password = "password1",
                PasswordExpirationDate = DateTime.UtcNow
            };
            clientUsers.Add(clientUser1);

            ClientUser clientUser2 = new ClientUser()
            {
                Id = Guid.NewGuid(),
                Password = "password2",
                PasswordExpirationDate = DateTime.UtcNow
            };
            clientUsers.Add(clientUser2);

            ClientUser clientUser3 = new ClientUser()
            {
                Id = Guid.NewGuid(),
                Password = "password3",
                PasswordExpirationDate = DateTime.UtcNow
            };
            clientUsers.Add(clientUser3);

            accountsRepository.SaveClientUsers(clientUsers);
            var clientUser = accountsRepository.GetClientUserByUserId(clientUser1.Id);

            Assert.IsNotNull(clientUser);

            Assert.AreEqual(clientUser.Id, clientUser1.Id);
            Assert.IsTrue(clientUser.PasswordExpirationDate.ToString(CultureInfo.InvariantCulture).Equals(clientUser1.PasswordExpirationDate.ToString(CultureInfo.InvariantCulture)));
        }

        [TestMethod]
        public void GetInvalidUsers()
        {
            var users = new List<User>()
            {
                new User()
                {
                    Id = Guid.NewGuid(),
                    AccountId = new Guid("8d8b02ac-66b7-4ea6-947b-4acc3dc914e5"),
                    FullName = "Test User Name1",
                    Status = Status.Inactive,
                    Email = null
                },
                new User()
                {
                    Id = Guid.NewGuid(),
                    AccountId = new Guid("8d8b02ac-66b7-4ea6-947b-4acc3dc914e5"),
                    FullName = "Test User Name2",
                    Status = Status.Inactive,
                    Email = null
                },
                new User()
                {
                    Id = Guid.NewGuid(),
                    AccountId = new Guid("8d8b02ac-66b7-4ea6-947b-4acc3dc914e5"),
                    FullName = "Test User Name3",
                    Status = Status.Inactive,
                    Email = "luciana.raduti@sustainalytics.com"
                },
                new User()
                {
                    Id = Guid.NewGuid(),
                    AccountId = new Guid("8d8b02ac-66b7-4ea6-947b-4acc3dc914e5"),
                    FullName = "Test User Name4",
                    Status = Status.Inactive,
                    Email = "luciana.raduti@sustainalytics.com"
                },
                new User()
                {
                    Id = Guid.NewGuid(),
                    AccountId = new Guid("8d8b02ac-66b7-4ea6-947b-4acc3dc914e5"),
                    FullName = "Test User Name5",
                    Status = Status.Inactive,
                    Email = "luciana.raduti@sustainalytics.com"
                }
            };
            _repository.SaveInvalidSyncUsers(users);

            var resultedNullUsers = _repository.GetInvalidSyncUsers(InvalidSynUserType.NullEmail);
            Assert.IsTrue(resultedNullUsers.Count() == 2);
            var nullUsers = new List<Guid>() { users[0].Id, users[1].Id };

            foreach (var resultedUser in resultedNullUsers)
            {
                Assert.IsTrue(nullUsers.Contains(resultedUser));
            }

            var resultedDuplicatedUsers = _repository.GetInvalidSyncUsers(InvalidSynUserType.Duplicate);
            Assert.IsTrue(resultedDuplicatedUsers.Count() == 3);
            var duplicatedUsers = new List<Guid>() { users[2].Id, users[3].Id, users[4].Id };

            foreach (var resultedUser in resultedDuplicatedUsers)
            {
                Assert.IsTrue(duplicatedUsers.Contains(resultedUser));
            }

            _repository.DeleteInvalidSyncUsers(users.Select(u => u.Id).ToList());

            var resultedUsers = _repository.GetInvalidSyncUsers(InvalidSynUserType.NullEmail);
            Assert.IsTrue(resultedUsers.Any() == false);

            resultedUsers = _repository.GetInvalidSyncUsers(InvalidSynUserType.Duplicate);
            Assert.IsTrue(resultedUsers.Any() == false);
        }
    }
}
